#include "StdAfx.h"
#include "Sample.h"
#include "Randomizer.h"
#include <math.h>

static Randomizer randomizer;


Sample::Sample(void)
{
}

Sample::Sample(unsigned int length)
{
	SetLength(length);
}

Sample::Sample(short* sample, unsigned int length)
{
	SetLength(length);
	short* buf = &data[0];
	for(unsigned int i=0; i<length; i++)
	{
		buf[i] = sample[i];
	}
}

Sample::~Sample(void)
{
}

static void CopyShort(short* origin, short* destination, unsigned int length, double multiplier)
{
	for(unsigned int i=0; i<length; i++)
	{
		double d = ((double)origin[i])*multiplier;
		destination[i] = (short)d;
	}
}

void Sample::CopyFrom(Sample* origin1, Sample* origin2)
{
	unsigned int length = origin1->GetLength();
	if(origin2!=NULL)
	{
		length += origin2->GetLength();
	}
	SetLength(length);
	CopyShort(origin1->GetData(), GetData(), origin1->GetLength(), 1.0);	
	if(origin2!=NULL)
	{
		short* destination = GetData();
		destination += sizeof(short)*origin1->GetLength();
		CopyShort(origin2->GetData(), destination, origin2->GetLength(), 1.0);
	}
}

void Sample::CopyTo(Sample& destination, double multiplier)
{
	destination.SetLength(data.size());
	CopyShort(GetData(), destination.GetData(), data.size(), multiplier);
}

void Sample::CopyToDouble(std::vector<double>& destination)
{
	unsigned int len = GetLength();
	short* data = GetData();
	destination.resize(len);
	for(unsigned int i=0; i<len; i++)
	{
		destination[i] = (double)data[i];
	}	
}

void Sample::LowPassFilter(Sample& destination, double gain, double neg, double pos)
{
	destination.SetLength(GetLength());
	double xv[3], yv[3];
	xv[0] = xv[1] = xv[2] = 0;
	yv[0] = yv[1] = yv[2] = 0;

	short* destData = destination.GetData();
	for (unsigned int i=0; i<GetLength(); i++)
    { 
		xv[0] = xv[1]; xv[1] = xv[2]; 
        xv[2] = (double)data[i] / gain;
        yv[0] = yv[1]; yv[1] = yv[2]; 
        yv[2] =   (xv[0] + xv[2]) + 2 * xv[1]
                     + ( neg * yv[0]) + (  pos * yv[1]);
        destData[i] = (short)yv[2];      
	}
}

void Sample::LowPass400Filter(Sample& destination)
{
//#define GAIN 1.513365061e+03
//#define NEG -0.9286270861
//#define POS 1.9259839697
	LowPassFilter(destination, 1.513365061e+03, -0.9286270861, 1.9259839697);
}

void Sample::LowPass6000Filter(Sample& destination)
{
//#define GAIN 6.847831502e+02
//#define NEG -0.8948743446
//#define POS 1.8890330794
	LowPassFilter(destination, 1.024264069e+01, -0.3333333333, 0.9428090416);
}

short* Sample::GetData()
{
	return &data[0];
}

unsigned int Sample::GetLength()
{
	return data.size();
}

void Sample::SetLength(unsigned int length)
{
	if(data.size()!=length)
	{
		data.resize(length);
	}
}

void Sample::Mix(std::vector<SampleDataProvider*>& providers, unsigned int length)
{
	double totalIntensity = 1;
	for(unsigned int i=0; i<providers.size(); i++)
	{
		totalIntensity += providers[i]->intensity;
	}
	SetLength(length);
	short* myBuf = GetData();
	for(unsigned int i=0; i<length; i++)
	{
		myBuf[i] = 0;
	}
	Sample tmpDest(length);
	short* tmpBuf = tmpDest.GetData();
	for(unsigned int i=0; i<providers.size(); i++)
	{
		SampleDataProvider* provider = providers[i];		
		provider->GetData(tmpBuf, length);
		double providerIntensity = ((double)provider->intensity/(double)totalIntensity)/2.0;
		for(unsigned int j=0; j<length; j++)
		{			
			short tmp = (short)(tmpBuf[j] * providerIntensity);
			myBuf[j] += tmp;
		}
	}
}

void Sample::Mix(std::vector<short*>& samples, std::vector<double>& intensities, unsigned int length)
{
	SetLength(length);
	short* myBuf = GetData();
	for(unsigned int i=0; i<samples.size(); i++)
	{
		short* curSample = samples[i];
		double intensity = intensities[i];
		for(unsigned int j=0; j<length; j++)
		{
			myBuf[j] += (short)(curSample[j]*intensity);
		}
	}
}

void Sample::AddWhiteNoise(double intensity)
{
	if(intensity>0.01)
	{
		short* data = GetData();
		for(unsigned int i=0; i<GetLength(); i++)
		{
			short add = (short)((double)randomizer.NextShort()*intensity);
			data[i] += add;
		}
	}
}

void Sample::AddBrownNoise(double intensity)
{
	if(intensity>0.01)
	{
		short* data = GetData();
		int add = 0, lastAdded=0;
		for(unsigned int i=0; i<GetLength(); i++)
		{
			do {
				short s = randomizer.NextShort();
				add = lastAdded + s;
			}
			while (add>SHORT_MAX || add<SHORT_MIN);
			double d = add*intensity;
			data[i] += (short)d;
			lastAdded = add;
		}
	}
}

void Sample::Silence()
{
	short* data = GetData();
	for(unsigned int i=0; i<GetLength(); i++)
	{
		data[i] = 0;
	}
}